from urllib.request import urlopen
from urllib.parse import urlencode
import urllib.error
import urllib.request
'''
urllib.request，用来打开和读取 url，意思就是可以用它来模拟发送请求，就像在浏览器里输入网址然后敲击回车一样，获取网页响应内容。
urllib.error，用来处理 urllib.request 引起的异常，保证程序的正常执行。
urllib.parse，用来解析 url，可以对 url 进行拆分、合并等。
urllib.robotparse，用来解析 robots.txt 文件，判断网站是否能够进行爬取
'''

# response = urlopen("https://www.baidu.com")
# print(response.read())

# POST请求
from urllib import parse
# data ={'kw' : 'python'}
# data = bytes(urlencode(data), encoding = 'utf-8')
# response = urlopen("https://fanyi.baidu.com/sug", data)
# print(response.read().decode('unicode_escape'))

# GET请求
# try:
#     response = urllib.request.urlopen('http://www.python.org',timeout=0.1)
#     print(response.read())
# except urllib.error.URLError as e:
#     print(e.reason)

# Request對象
from urllib import request,parse
# url = 'http://www.python.org'
# headers = {
#     'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'
# }
# req = request.Request(url, headers = headers, method = 'GET')
# response = request.urlopen(req)
# print(response.read())

# 采用 HTTP POST 请求的方法模拟谷歌浏览器访问网站，输出响应上下文
# url = 'https://fanyi.baidu.com/sug'
# data = {'kw' : 'python'}
# data = bytes(parse.urlencode(data), encoding = 'utf-8')
# headers = {
#     'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'
# }
# req = request.Request(url, data, headers, method = 'POST')
# response = request.urlopen(req)
# print(response.read().decode('unicode_escape'))

# 创建一个 HTTP GET 请求，通过 add_header 添加一个 UserAgent
# import random
# url = 'http://www.python.org'
# headerUserAgentList = ['Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36',
# 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0']
# randomHeaderUserAgent = random.choice(headerUserAgentList) # 随机选取一个 UserAgent
# req = urllib.request.Request(url)
# req.add_header('User-Agent', randomHeaderUserAgent) # 添加 UserAgent
# response=urllib.request.urlopen(req)
# print(req.get_header('User-agent'))
# print(req.headers) # 打印请求的 header 信息

# tODO urllib.error 模块定义了由 urllib.request 模块引发的异常，异常主要包含 URLError 和 HTTPError。
# URLError 在请求连接时候捕获网址错误引发的异常 URLError 只有一个属性 reason
# from urllib import request, error
# try:
#     response = request.urlopen('https://www,baidu,com')
# except error.URLError as e:
#     print(e.reason)
# HTTPError  HTTPError 是 URLError 的子类，专门用来处理 HTTP 请求错误
# 返回401未授权错误
# from urllib import request,error
# try:
#     response=request.urlopen('http://pythonscraping.com/pages/auth/login.php')
#     print(response.getcode())
# except error.HTTPError as e:
#     print('1.错误原因：\n%s\n2.状态码：\n%s\n3.响应头信息：\n%s' %(e.reason, e.code, e.headers))
# except error.URLError as e:
#     print(e.reason)

# TODO urllib.parse 模块定义了一个处理 url 的标准接口，用来实现 url 字符串的抽取、合并以及链接转换。该模块主要用到的函数如下
'''
urlstring，待解析的 url 字符串。
scheme，是默认的协议，比如 http 或者 https，url 字符串中如果不携带相关协议，可以通过 scheme 来指定，如果 url 中指定了相关协议则在 url 中生效。
allow_fragments，是否忽略锚点，设置为 False 即 fragment 部分会被忽略，反之不会忽略。
'''

# 解析并输出 url 中每个字段的字符串
# import urllib
# url = 'http://www.baidu.com/urllib.parse.html;python?kw=urllib.parse#module-urllib'
# result = urllib.parse.urlparse(url)
# print(result)
# print(result.scheme, result.netloc, result.path, result.params, result.query, result.fragment, sep = '\n')

# 与 urlparse 相反，通过列表或者元祖的形式将分段的字符串组合成一个完整的 url 字符串。 parts，可以是列表或者元组。
# 通过 data 列表或元组构造一个 url 并输出
# import urllib
# dataList = ['http', 'www.baidu.com', '/urllib.parse.html', 'python', 'kw=urllib.parse', 'modul-urllib'] # 六个字符串都必须填写，否则会出现 ValueError 错误，如果某一字符串不存在则填入空字符
# dataTuple = ('http', 'www.baidu.com', '/urllib.parse.html', '', 'kw=urllib.parse', 'modul-urllib') # 六个字符串都必须填写，否则会出现 ValueError 错误，如果某一字符串不存在则填入空字符
# urlList = urllib.parse.urlunparse(dataList)
# urlTuple = urllib.parse.urlunparse(dataTuple)
# print('1.urlList:%s\n2.urlTuple:%s' % (urlList, urlTuple))

# 与 urlparse 函数类似，但它只返回 url 字符串的5个字段，把 params 合并到 path 中
# urlsplit 解析并输出 url 中每个字段的字符串，params 会合并到 path 中。
# import urllib
# url = 'http://www.baidu.com/urllib.parse.html;python?kw=urllib.parse#modul-urllib'
# result = urllib.parse.urlsplit(url)
# print(result)
# print(result.scheme, result.netloc, result.path, result.query, result.fragment, sep = '\n')
# urlunsplit 与 urlunparse 函数类似，它也是将 url 中各部分字段组合完整的 url 字符串的方法，唯一的区别是列表或元组的长度必须是5个，因为它把 params 省略了。
# 通过 data 列表或元组构造一个 url 并输出
# import urllib
# dataList = ['http', 'www.baidu.com', '/urllib.parse.html;python', 'kw=urllib.parse', 'modul-urllib'] # 五个字符串都必须填写，否则会出现 ValueError 错误，如果某一字符串不存在则填入空字符
# dataTuple = ('http', 'www.baidu.com', '/urllib.parse.html;python', 'kw=urllib.parse', 'modul-urllib') # 五个字符串都必须填写，否则会出现 ValueError 错误，如果某一字符串不存在则填入空字符
# urlList = urllib.parse.urlunsplit(dataList)
# urlTuple = urllib.parse.urlunsplit(dataTuple)
# print('1.urlList:%s\n2.urlTuple:%s' % (urlList, urlTuple))

# 使用 %xx 转义字符替换字符串中的特殊字符，比如汉字。字母、数字和‘_.-~’字符不会被替换。
'''
string，可以是 str 字符串或 bytes 类型。
safe，可选参数，默认是’/’，指明不应该被替换的附加 ASCII 字符。
encoding 和 errors，可选参数，用来定义如何处理 non-ASCII 字符。
一般默认设置编码方法为 encoding=’utf-8’，errors=’strict’，
这意味着编码错误将引发 UnicodeError。如果 string 是 bytes 类型，不能设置 encoding 和 errors，否则将引发 TypeError。
'''
# 采用 quote 对 url 中的汉字进行编码，输出编码后的结果
# import urllib
# url = 'http://www.baidu.com/爬虫'
# result = urllib.parse.quote(url)
# print(result)
# url = 'http://www.baidu.com/+爬虫'
# result = urllib.parse.quote(url, '+') # 更改 safe 参数
# print(result)

# 解码经过 quote 函数处理后的 url，输出解码后的结果。 与 quote 函数相反，把 %xx 转义字符替换成字符。
'''
string，必须是 str 字符串。
encoding 和 errors，可选参数，定义如何将 %xx 转义字符解码为 Unicode 字符。encoding 默认为 ‘utf-8’，errors 默认为 ‘replace’，表示无效的转义字符将会用占位符替换
'''
# import urllib
# url = 'http://www.baidu.com/爬虫'
# result = urllib.parse.quote(url)
# print(result)
# result = urllib.parse.unquote(url)
# print(result)

''' urllib.parse.urljoin(base, url, allow_fragments=True)
urljoin 该函数用来将基本 url 与另一个 url 组合，更新基本 url 字符串。它会使用 url 对基本 url 中缺失的部分进行补充，
比如 scheme (协议)、netloc (域名)和 path (路径)。即根据 url 字符串中带有的字段，对基本 url 中没有的字段进行补充，已存在的字段进行替换
base，是一个基本 url。
url，将 scheme (协议)、netloc (域名)或 path (路径)字段组合进基本 url 的 url。
allow_fragments，是否忽略锚点，设置为 False 即 fragment 部分会被忽略，反之不会忽略。
'''
# 基于 url 对 base_url 进行重新组合，并输出组合结果。
# import urllib
# base_url = 'http://www.baidu.com'
# url = 'https://www.google.com/urllib.parse.html;python?kw=urllib.parse#module-urllib'
# result = urllib.parse.urljoin(base_url,url,False)
# print(result)

'''
urllib.parse.urlencode(query, doseq=False, safe=’’, encoding=None, errors=None, quote_via=quote_plus)
urlencode 函数可以将字典转化为 GET 请求中的 query (查询条件)，或者将字典转化为 POST 请求中需要上传的数据
query，字典类型。
doseq，允许字典中一个键对应多个值，编码成 query (查询条件)。
safe、encoding 和 errors，这三个参数由 quote_via 指定
'''
# 创建 GET 请求 TODO 实际开发中回使用到
# import urllib
# params = {'username':'xxx','password':'123'}
# base_url='http://www.baidu.com'
# url=base_url + '?' + urllib.parse.urlencode(params)
# print(url)
# params = {'username':['xxx', 'yyy'], 'password':'123'} # username 键对应多个值
# base_url='http://www.baidu.com'
# url=base_url + '?' + urllib.parse.urlencode(params) # doseq 设置为 False，会解析成乱码
# print(url)
# url=base_url + '?' + urllib.parse.urlencode(params, True) # doseq 设置为 True
# print(url)

# rebotparser 模块提供了一个 RobotFileParser 类，主要用来解析网站上发布的 robots.txt，然后根据解析内容判断爬虫是否有权限来爬取这个网页。
'''
robots.txt，存放于网站根目录下，采用 ASCII 编码的文本文件，记录此网站中的哪些内容是不应被爬虫获取的，哪些是可以被爬虫获取的。

robots.txt 文件内容举例
User-agent: * Disallow: / Allow: /public/

User-agent，爬虫的名称，将其设置为 * 代表协议对任何爬虫有效，如果设置为 Baiduspider 则代表协议仅对百度爬虫有效，要是有多条则对多个爬虫有效，至少需要指定一条。
Disallow，网页中不允许抓取的目录，上述例子中设置的 / 代表不允许抓取所有的页面。
Allow，一般和 Disallow 一起使用，用来排除单独的某些限制，上述例子中设置为 /public/ 表示所有页面不允许抓取，但可以抓取 public 目录。
'''


'''
urllib.robotparser.RobotFileParser(url=’’) 类及其常用方法
set_url(url)，设置引用 robots.txt 文件的 url，如果在创建 RobotFileParser 对象时传入了 url，那么就不需要使用这个方法设置 url。
read()，读取 robots.txt 文件并将其提供给解析器，不返回任何内容。
parse(lines)，用来解析 robots.txt 某些行的内容，并安装语法规则来分析内容。
can_fetch(useragent, url)，传入两个参数，用户代理以及要爬取的网站，返回的内容是该用户代理否可以抓取这个网站，结果为 True 或 False
'''
# 使用两种爬虫代理分别查看是否可以对 'http://www.baidu.com' 网站进行爬取
from urllib.robotparser import RobotFileParser
rp = RobotFileParser()
rp.set_url("http://www.baidu.com/robots.txt")
rp.read()
print(rp.can_fetch('Baiduspider', 'http://www.baidu.com'))
print(rp.can_fetch('*', 'http://www.baidu.com'))